package com.googlecode.continuity.core.client;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.googlecode.continuity.core.client.rpc.*;

/**
 * CoreGWT provides centralised services for the GWT modules in Continuity.  This includes an RPC registry, formatting
 * aids and central path information.
 * <p/>
 * todo: make available for dependency injection
 * todo: use class literals for rpc registry
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class CoreGWT implements EntryPoint {
  private static ContinuityUser currentUser;
  private static Map rpcServices = new HashMap();
  private static Map rpcCreators = new HashMap();
  private static final String WEBAPP_ROOT = "/continuity/";
  private static final DateTimeFormat localeFormat = DateTimeFormat.getFormat("dd.MM.yy, HH:mm");

  /**
   * Returns the RPC described by the given id if it is registered and throws a <code>RuntimeException</code> otherwise.
   * The id is made up from the string literal of the asynchronous implementation's name, for example when using the
   * <code>UserRPC</code>, the literal would be <code>"UserRPCAsync"</code>.
   *
   * If the requested RPC is registered but has not been retrieved before, it is created by using the
   * {@link com.googlecode.continuity.core.client.rpc.RPCCreationCallback#createRPC()} method.
   *
   * @param id name of the RPC to be retrieved
   * @return an RPC object
   */
  public static Object getRPC(String id) {
    if (!rpcServices.containsKey(id)) {
      if (!rpcCreators.containsKey(id)) {
        throw new RuntimeException("Failed to create RPC: No creator for the class [" + id +
            "] is registered with the RPC registry.");
      }
      rpcServices.put(id, ((RPCCreationCallback) rpcCreators.get(id)).createRPC());
    }
    return rpcServices.get(id);
  }

  /**
   * Registers an {@link com.googlecode.continuity.core.client.rpc.RPCCreationCallback} with the RPC registry, using the
   * RPC's asynchronous implementation's interface name as id. For example, when using the <code>UserRPC</code>, the
   * literal would be <code>"UserRPCAsync"</code>.
   *
   * The RPC object is created by the <code>RPCCreationCallback</code> once the registry receives a request for it.
   *
   * @param id name of the ROC to be registered
   * @param callback object that allows the construction of the RPC object
   */
  public static void registerRPC(String id, RPCCreationCallback callback) {
    rpcCreators.put(id, callback);
  }

  /**
   * Returns the root path for this application.
   *
   * @return this application's root path
   */
  public static String getWebappRoot() {
    return WEBAPP_ROOT;
  }

  /**
   * Main startup method of Continuity's GWT package.  Initialises RPCs for User, Configuration and Groups and retrieves
   * current user.
   */
  public void onModuleLoad() {
    CoreGWT.registerRPC("UserRPCAsync", new RPCCreationCallback() {
      public Object createRPC() {
        UserRPCAsync userRPC = (UserRPCAsync) GWT.create(UserRPC.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) userRPC;
        String moduleRelativeURL = CoreGWT.getWebappRoot() + "user.rpc";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return userRPC;
      }
    });

    CoreGWT.registerRPC("GroupRPCAsync", new RPCCreationCallback() {
      public Object createRPC() {
        GroupRPCAsync groupRPC = (GroupRPCAsync) GWT.create(GroupRPC.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) groupRPC;
        String moduleRelativeURL = CoreGWT.getWebappRoot() + "group.rpc";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return groupRPC;
      }
    });

    CoreGWT.registerRPC("ConfigurationRPCAsync", new RPCCreationCallback() {
      public Object createRPC() {
        ConfigurationRPCAsync configRPC = (ConfigurationRPCAsync) GWT.create(ConfigurationRPC.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) configRPC;
        String moduleRelativeURL = CoreGWT.getWebappRoot() + "configuration.rpc";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return configRPC;
      }
    });

    ((UserRPCAsync) getRPC("UserRPCAsync")).getCurrentUser(new AsyncCallback() {
      public void onFailure(Throwable throwable) {
        // todo: error
      }

      public void onSuccess(Object o) {
        currentUser = (ContinuityUser) o;
      }
    });

  }

  /**
   * Returns a date and time formatting object for the current locale.
   * @return date and time formatting object
   */
  public static DateTimeFormat getLocaleDateFormat() {
    return localeFormat;
  }

  /**
   * Returns the user currently signed on or null if no user is signed on.
   *
   * @return the user currently signed on or null if no user is signed on
   */
  public static ContinuityUser getCurrentUser() {
    return currentUser;
  }
}
